home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / tls / tls085.solintel.Z / tls085.solintel / usr / lib / tcl / tcl.tlib < prev    next >
Encoding:
Text File  |  1995-07-20  |  34.3 KB  |  1,385 lines

  1.  
  2. #@package: TclX-ArrayProcedures for_array_keys
  3.  
  4. proc for_array_keys {varName arrayName codeFragment} {
  5.     upvar $varName enumVar $arrayName enumArray
  6.  
  7.     if ![info exists enumArray] {
  8.     error "\"$arrayName\" isn't an array"
  9.     }
  10.  
  11.     set searchId [array startsearch enumArray]
  12.     while {[array anymore enumArray $searchId]} {
  13.     set enumVar [array nextelement enumArray $searchId]
  14.     uplevel $codeFragment
  15.     }
  16.     array donesearch enumArray $searchId
  17. }
  18.  
  19. #@package: TclX-Compatibility execvp assign_fields
  20.  
  21. proc execvp {progname args} {
  22.     error "The execvp command is outdated, use the execl command directly"
  23. }
  24. proc assign_fields {list args} {
  25.     if [lempty $args] {
  26.         return
  27.     }
  28.     return [uplevel lassign [list $list] $args]
  29. }
  30.  
  31. #@package: TclX-convertlib convert_lib
  32.  
  33.  
  34. proc tclx:ParseTclIndex {tclIndex fileTblVar ignore} {
  35.     upvar $fileTblVar fileTbl
  36.     set allOK 1
  37.  
  38.  
  39.     set tclIndexFH [open $tclIndex r]
  40.     set hdr [gets $tclIndexFH]
  41.     if {$hdr != "# Tcl autoload index file, version 2.0"} {
  42.         error "can only convert version 2.0 Tcl auto-load files"
  43.     }
  44.     set dir [file dirname $tclIndex]  ;# Expected by the script.
  45.     eval [read $tclIndexFH]
  46.     close $tclIndexFH
  47.  
  48.     foreach procName [array names auto_index] {
  49.         if ![string match "source *" $auto_index($procName)] {
  50.             puts stderr "WARNING: Can't convert load command for \"$procName\": $auto_index($procName)"
  51.             set allOK 0
  52.             continue
  53.         }
  54.         set filePath [lindex $auto_index($procName) 1]
  55.         set fileName [file tail $filePath] 
  56.         if {[lsearch $ignore $fileName] >= 0} continue
  57.  
  58.         lappend fileTbl($filePath) $procName
  59.     }
  60.     if ![info exists fileTbl] {
  61.         error "no entries could be converted in $tclIndex"
  62.     }
  63.     return $allOK
  64. }
  65.  
  66.  
  67. proc convert_lib {tclIndex packageLib {ignore {}}} {
  68.     source [info library]/buildidx.tcl
  69.  
  70.     if {[file tail $tclIndex] != "tclIndex"} {
  71.         error "Tail file name must be `tclIndex': $tclIndex"}
  72.     if ![file readable $tclIndex] {
  73.         error "File not readable: $tclIndex"
  74.     }
  75.  
  76.  
  77.     set tclIndex [glob $tclIndex]
  78.     if ![string match "/*" $tclIndex] {
  79.         set tclIndex "[pwd]/$tclIndex"
  80.     }
  81.  
  82.  
  83.     set allOK [tclx:ParseTclIndex $tclIndex fileTbl $ignore]
  84.  
  85.  
  86.     if {[file extension $packageLib] != ".tlib"} {
  87.         append packageLib ".tlib"
  88.     }
  89.     set libFH [open $packageLib w]
  90.  
  91.     foreach srcFile [array names fileTbl] {
  92.         set pkgName [file tail [file dirname $srcFile]]/[file tail [file root $srcFile]]
  93.         set srcFH [open $srcFile r]
  94.         puts $libFH "#@package: $pkgName $fileTbl($srcFile)\n"
  95.         copyfile $srcFH $libFH
  96.         close $srcFH
  97.     }
  98.     close $libFH
  99.     buildpackageindex $packageLib
  100.     if !$allOK {
  101.         error "*** Not all entries converted, but library generated"
  102.     }
  103. }
  104.  
  105. #@package: TclX-developer_utils saveprocs edprocs
  106.  
  107. proc saveprocs {fileName args} {
  108.     set fp [open $fileName w]
  109.     puts $fp "# tcl procs saved on [fmtclock [getclock]]\n"
  110.     puts $fp [eval "showproc $args"]
  111.     close $fp
  112. }
  113.  
  114. proc edprocs {args} {
  115.     global env
  116.  
  117.     set tmpFilename /tmp/tcldev.[id process]
  118.  
  119.     set fp [open $tmpFilename w]
  120.     puts $fp "\n# TEMP EDIT BUFFER -- YOUR CHANGES ARE FOR THIS SESSION ONLY\n"
  121.     puts $fp [eval "showproc $args"]
  122.     close $fp
  123.  
  124.     if [info exists env(EDITOR)] {
  125.         set editor $env(EDITOR)
  126.     } else {
  127.     set editor vi
  128.     }
  129.  
  130.     set startMtime [file mtime $tmpFilename]
  131.     system "$editor $tmpFilename"
  132.  
  133.     if {[file mtime $tmpFilename] != $startMtime} {
  134.     source $tmpFilename
  135.     echo "Procedures were reloaded."
  136.     } else {
  137.     echo "No changes were made."
  138.     }
  139.     unlink $tmpFilename
  140.     return
  141. }
  142.  
  143. #@package: TclX-forfile for_file
  144.  
  145. proc for_file {var filename code} {
  146.     upvar $var line
  147.     set fp [open $filename r]
  148.     while {[gets $fp line] >= 0} {
  149.         uplevel $code
  150.     }
  151.     close $fp
  152. }
  153.  
  154.  
  155. #@package: TclX-globrecur recursive_glob
  156.  
  157. proc recursive_glob {dirlist globlist} {
  158.     set result {}
  159.     set recurse {}
  160.     foreach dir $dirlist {
  161.         if ![file isdirectory $dir] {
  162.             error "\"$dir\" is not a directory"
  163.         }
  164.         foreach pattern $globlist {
  165.             set result [concat $result [glob -nocomplain -- $dir/$pattern]]
  166.         }
  167.         foreach file [glob -nocomplain -- $dir/* $dir/.*] {
  168.             if [file isdirectory $file] {
  169.                 set fileTail [file tail $file]
  170.                 if {!(($fileTail == ".") || ($fileTail == ".."))} {
  171.                     lappend recurse $file
  172.                 }
  173.             }
  174.         }
  175.     }
  176.     if ![lempty $recurse] {
  177.         set result [concat $result [recursive_glob $recurse $globlist]]
  178.     }
  179.     return $result
  180. }
  181.  
  182. #@package: TclX-forrecur for_recursive_glob
  183.  
  184. proc for_recursive_glob {var dirlist globlist code {depth 1}} {
  185.     upvar $depth $var myVar
  186.     set recurse {}
  187.     foreach dir $dirlist {
  188.         if ![file isdirectory $dir] {
  189.             error "\"$dir\" is not a directory"
  190.         }
  191.         foreach pattern $globlist {
  192.             foreach file [glob -nocomplain -- $dir/$pattern] {
  193.                 set myVar $file
  194.                 uplevel $depth $code
  195.             }
  196.         }
  197.         foreach file [glob -nocomplain -- $dir/* $dir/.*] {
  198.             if [file isdirectory $file] {
  199.                 set fileTail [file tail $file]
  200.                 if {!(($fileTail == ".") || ($fileTail == ".."))} {
  201.                     lappend recurse $file
  202.                 }
  203.             }
  204.         }
  205.     }
  206.     if ![lempty $recurse] {
  207.         for_recursive_glob $var $recurse $globlist $code [expr {$depth + 1}]
  208.     }
  209.     return {}
  210. }
  211.  
  212. #@package: TclX-help help helpcd helppwd apropos
  213.  
  214.  
  215. proc help:RootDirs {} {
  216.     global auto_path
  217.     set roots {}
  218.     foreach dir $auto_path {
  219.         if [file isdirectory $dir/help] {
  220.             lappend roots $dir/help
  221.         }
  222.     }
  223.     return $roots
  224. }
  225.  
  226.  
  227. proc help:FlattenPath pathName {
  228.     set newPath {}
  229.     foreach element [split $pathName /] {
  230.         if {"$element" == "." || [lempty $element]} continue
  231.  
  232.         if {"$element" == ".."} {
  233.             if {[llength [join $newPath /]] == 0} {
  234.                 error "Help: name goes above subject directory root"}
  235.             lvarpop newPath [expr [llength $newPath]-1]
  236.             continue
  237.         }
  238.         lappend newPath $element
  239.     }
  240.     set newPath [join $newPath /]
  241.  
  242.  
  243.     if {("$newPath" == "") && [string match "/*" $pathName]} {
  244.         set newPath "/"
  245.     }
  246.         
  247.     return $newPath
  248. }
  249.  
  250.  
  251. proc help:ConvertPath pathName {
  252.     global TCLXENV
  253.  
  254.     if {![string match "/*" $pathName]} {
  255.         if {"$TCLXENV(help:curSubject)" == "/"} {
  256.             set pathName "/$pathName"
  257.         } else {
  258.             set pathName "$TCLXENV(help:curSubject)/$pathName"
  259.         }
  260.     }
  261.     set pathName [help:FlattenPath $pathName]
  262.  
  263.  
  264.     if {$pathName == "/"} {
  265.         return [help:RootDirs]
  266.     }
  267.  
  268.  
  269.     foreach dir [help:RootDirs] {
  270.         if [file readable $dir/$pathName] {
  271.             return [list $dir/$pathName]
  272.         }
  273.     }
  274.     error "\"$pathName\" does not exist"
  275. }
  276.  
  277.  
  278. proc help:RelativePath pathName {
  279.     foreach dir [help:RootDirs] {
  280.         if {[csubstr $pathName 0 [clength $dir]] == $dir} {
  281.             set name [csubstr $pathName [clength $dir] end]
  282.             if {$name == ""} {set name /}
  283.             return $name
  284.         }
  285.     }
  286.     if ![info exists found] {
  287.         error "problem translating \"$pathName\""
  288.     }
  289.  
  290. }
  291.  
  292.  
  293. proc help:ListSubject {pathName pathList subjectsVar pagesVar} {
  294.     upvar $subjectsVar subjects $pagesVar pages
  295.  
  296.     set subjects {}
  297.     set pages {}
  298.     set foundDir 0
  299.     foreach dir $pathList {
  300.         if ![file isdirectory $dir] continue
  301.         set foundDir 1
  302.         foreach file [glob -nocomplain $dir/*] {
  303.             if [string match *.brf $file] continue
  304.             if [file isdirectory $file] {
  305.                 lappend subjects [file tail $file]/
  306.             } else {
  307.                 lappend pages [file tail $file]
  308.             }
  309.         }
  310.     }
  311.     if !$foundDir {
  312.         error "\"$pathName\" is not a subject"
  313.     }
  314.     set subjects [lsort $subjects]
  315.     set pages [lsort $pages]
  316.     return {}
  317. }
  318.  
  319.  
  320. proc help:Display line {
  321.     global TCLXENV
  322.     if {$TCLXENV(help:lineCnt) >= 23} {
  323.         set TCLXENV(help:lineCnt) 0
  324.         puts stdout ":" nonewline
  325.         flush stdout
  326.         gets stdin response
  327.         if {![lempty $response]} {
  328.             return 0}
  329.     }
  330.     puts stdout $line
  331.     incr TCLXENV(help:lineCnt)
  332. }
  333.  
  334.  
  335. proc help:DisplayPage filePath {
  336.  
  337.     set inFH [open $filePath r]
  338.     while {[gets $inFH fileBuf] >= 0} {
  339.         if {![help:Display $fileBuf]} {
  340.             break}
  341.     }
  342.     close $inFH
  343. }    
  344.  
  345.  
  346. proc help:DisplayColumns {nameList} {
  347.     set count 0
  348.     set outLine ""
  349.     foreach name $nameList {
  350.         if {$count == 0} {
  351.             append outLine "   "}
  352.         append outLine $name
  353.         if {[incr count] < 4} {
  354.             set padLen [expr 17-[clength $name]]
  355.             if {$padLen < 3} {
  356.                set padLen 3}
  357.             append outLine [replicate " " $padLen]
  358.         } else {
  359.            if {![help:Display $outLine]} {
  360.                return}
  361.            set outLine ""
  362.            set count 0
  363.         }
  364.     }
  365.     if {$count != 0} {
  366.         help:Display [string trimright $outLine]}
  367.     return
  368. }
  369.  
  370.  
  371. proc help:HelpOnHelp {} {
  372.     set helpPage [lindex [help:ConvertPath /help] 0]
  373.     if [lempty $helpPage] {
  374.         error "No help page on help found"
  375.     }
  376.     help:DisplayPage $helpPage
  377. }
  378.  
  379.  
  380. proc help {{what {}}} {
  381.     global TCLXENV
  382.  
  383.     set TCLXENV(help:lineCnt) 0
  384.  
  385.  
  386.     if {($what == "help") || ($what == "?")} {
  387.         help:HelpOnHelp
  388.         return
  389.     }
  390.  
  391.     set pathList [help:ConvertPath $what]
  392.     if [file isfile [lindex $pathList 0]] {
  393.         help:DisplayPage [lindex $pathList 0]
  394.         return
  395.     }
  396.  
  397.     help:ListSubject $what $pathList subjects pages
  398.     set relativeDir [help:RelativePath [lindex $pathList 0]]
  399.  
  400.     if {[llength $subjects] != 0} {
  401.         help:Display "\nSubjects available in $relativeDir:"
  402.         help:DisplayColumns $subjects
  403.     }
  404.     if {[llength $pages] != 0} {
  405.         help:Display "\nHelp pages available in $relativeDir:"
  406.         help:DisplayColumns $pages
  407.     }
  408. }
  409.  
  410.  
  411.  
  412. proc helpcd {{dir /}} {
  413.     global TCLXENV
  414.  
  415.     set pathName [lindex [help:ConvertPath $dir] 0]
  416.  
  417.     if {![file isdirectory $pathName]} {
  418.         error "Helpcd: \"$dir\" is not a subject"}
  419.  
  420.     set TCLXENV(help:curSubject) [help:RelativePath $pathName]
  421.     return
  422. }
  423.  
  424.  
  425. proc helppwd {} {
  426.         global TCLXENV
  427.         echo "Current help subject: $TCLXENV(help:curSubject)"
  428. }
  429.  
  430.  
  431. proc apropos {regexp} {
  432.     global TCLXENV
  433.  
  434.     set TCLXENV(help:lineCnt) 0
  435.  
  436.     set ch [scancontext create]
  437.     scanmatch -nocase $ch $regexp {
  438.         set path [lindex $matchInfo(line) 0]
  439.         set desc [lrange $matchInfo(line) 1 end]
  440.         if {![help:Display [format "%s - %s" $path $desc]]} {
  441.             set stop 1
  442.             return}
  443.     }
  444.     set stop 0
  445.     foreach dir [help:RootDirs] {
  446.         foreach brief [glob -nocomplain $dir/*.brf] {
  447.             set briefFH [open $brief]
  448.             scanfile $ch $briefFH
  449.             close $briefFH
  450.             if $stop break
  451.         }
  452.         if $stop break
  453.     }
  454.     scancontext delete $ch
  455. }
  456.  
  457. global TCLXENV
  458.  
  459. set TCLXENV(help:curSubject) "/"
  460.  
  461. #@package: TclX-profrep profrep
  462.  
  463. proc profrep:summarize {profDataVar stackDepth sumProfDataVar} {
  464.     upvar $profDataVar profData $sumProfDataVar sumProfData
  465.  
  466.     if {(![info exists profData]) || ([catch {array size profData}] != 0)} {
  467.         error "`profDataVar' must be the name of an array returned by the `profile off' command"
  468.     }
  469.     set maxNameLen 0
  470.     foreach procStack [array names profData] {
  471.         foreach procName $procStack {
  472.             set maxNameLen [max $maxNameLen [clength $procName]]
  473.         }
  474.         if {[llength $procStack] < $stackDepth} {
  475.             set sigProcStack $procStack
  476.         } else {
  477.             set sigProcStack [lrange $procStack 0 [expr {$stackDepth - 1}]]
  478.         }
  479.         if [info exists sumProfData($sigProcStack)] {
  480.             set cur $sumProfData($sigProcStack)
  481.             set add $profData($procStack)
  482.             set     new [expr [lindex $cur 0]+[lindex $add 0]]
  483.             lappend new [expr [lindex $cur 1]+[lindex $add 1]]
  484.             lappend new [expr [lindex $cur 2]+[lindex $add 2]]
  485.             set sumProfData($sigProcStack) $new
  486.         } else {
  487.             set sumProfData($sigProcStack) $profData($procStack)
  488.         }
  489.     }
  490.     return $maxNameLen
  491. }
  492.  
  493. proc profrep:sort {sumProfDataVar sortKey} {
  494.     upvar $sumProfDataVar sumProfData
  495.  
  496.     case $sortKey {
  497.         {calls} {set keyIndex 0}
  498.         {real}  {set keyIndex 1}
  499.         {cpu}   {set keyIndex 2}
  500.         default {
  501.             error "Expected a sort type of: `calls', `cpu' or ` real'"}
  502.     }
  503.  
  504.  
  505.     foreach procStack [array names sumProfData] {
  506.         set key [format "%016d" [lindex $sumProfData($procStack) $keyIndex]]
  507.         lappend keyProcList [list $key $procStack]
  508.     }
  509.     set keyProcList [lsort $keyProcList]
  510.  
  511.  
  512.     for {set idx [expr [llength $keyProcList]-1]} {$idx >= 0} {incr idx -1} {
  513.         lappend sortedProcList [lindex [lindex $keyProcList $idx] 1]
  514.     }
  515.     return $sortedProcList
  516. }
  517.  
  518.  
  519. proc profrep:print {sumProfDataVar sortedProcList maxNameLen outFile
  520.                     userTitle} {
  521.     upvar $sumProfDataVar sumProfData
  522.     
  523.     if {$outFile == ""} {
  524.         set outFH stdout
  525.     } else {
  526.         set outFH [open $outFile w]
  527.     }
  528.  
  529.  
  530.     set stackTitle "Procedure Call Stack"
  531.     set maxNameLen [max [expr $maxNameLen+6] [expr [clength $stackTitle]+4]]
  532.     set hdr [format "%-${maxNameLen}s %10s %10s %10s" $stackTitle \
  533.                     "Calls" "Real Time" "CPU Time"]
  534.     if {$userTitle != ""} {
  535.         puts $outFH [replicate - [clength $hdr]]
  536.         puts $outFH $userTitle
  537.     }
  538.     puts $outFH [replicate - [clength $hdr]]
  539.     puts $outFH $hdr
  540.     puts $outFH [replicate - [clength $hdr]]
  541.  
  542.  
  543.     foreach procStack $sortedProcList {
  544.         set data $sumProfData($procStack)
  545.         puts $outFH [format "%-${maxNameLen}s %10d %10d %10d" \
  546.                             [lvarpop procStack] \
  547.                             [lindex $data 0] [lindex $data 1] [lindex $data 2]]
  548.         foreach procName $procStack {
  549.             if {$procName == "<global>"} break
  550.             puts $outFH "    $procName"
  551.         }
  552.     }
  553.     if {$outFile != ""} {
  554.         close $outFH
  555.     }
  556. }
  557.  
  558.  
  559. proc profrep {profDataVar sortKey stackDepth {outFile {}} {userTitle {}}} {
  560.     upvar $profDataVar profData
  561.  
  562.     set maxNameLen [profrep:summarize profData $stackDepth sumProfData]
  563.     set sortedProcList [profrep:sort sumProfData $sortKey]
  564.     profrep:print sumProfData $sortedProcList $maxNameLen $outFile $userTitle
  565.  
  566. }
  567.  
  568. #@package: TclX-directory_stack pushd popd dirs
  569.  
  570. global TCLXENV(dirPushList)
  571.  
  572. set TCLXENV(dirPushList) ""
  573.  
  574. proc pushd {args} {
  575.     global TCLXENV
  576.  
  577.     if {[llength $args] > 1} {
  578.         error "bad # args: pushd [dir_to_cd_to]"
  579.     }
  580.     set TCLXENV(dirPushList) [linsert $TCLXENV(dirPushList) 0 [pwd]]
  581.  
  582.     if {[llength $args] != 0} {
  583.         cd [glob $args]
  584.     }
  585. }
  586.  
  587. proc popd {} {
  588.     global TCLXENV
  589.  
  590.     if [llength $TCLXENV(dirPushList)] {
  591.         cd [lvarpop TCLXENV(dirPushList)]
  592.         pwd
  593.     } else {
  594.         error "directory stack empty"
  595.     }
  596. }
  597.  
  598. proc dirs {} { 
  599.     global TCLXENV
  600.     echo [pwd] $TCLXENV(dirPushList)
  601. }
  602.  
  603. #@package: TclX-set_functions union intersect intersect3 lrmdups
  604.  
  605. proc union {lista listb} {
  606.     set full_list [lsort [concat $lista $listb]]
  607.     set check_element [lindex $full_list 0]
  608.     set outlist $check_element
  609.     foreach element [lrange $full_list 1 end] {
  610.     if {$check_element == $element} continue
  611.     lappend outlist $element
  612.     set check_element $element
  613.     }
  614.     return $outlist
  615. }
  616.  
  617. proc lrmdups list {
  618.     if [lempty $list] {
  619.         return {}
  620.     }
  621.     set list [lsort $list]
  622.     set last [lvarpop list]
  623.     lappend result $last
  624.     foreach element $list {
  625.     if {$last != $element} {
  626.         lappend result $element
  627.         set last $element
  628.     }
  629.     }
  630.     return $result
  631. }
  632.  
  633.  
  634. proc intersect3 {list1 list2} {
  635.     set list1Result ""
  636.     set list2Result ""
  637.     set intersectList ""
  638.  
  639.     set list1 [lrmdups $list1]
  640.     set list2 [lrmdups $list2]
  641.  
  642.     while {1} {
  643.         if [lempty $list1] {
  644.             if ![lempty $list2] {
  645.                 set list2Result [concat $list2Result $list2]
  646.             }
  647.             break
  648.         }
  649.         if [lempty $list2] {
  650.         set list1Result [concat $list1Result $list1]
  651.             break
  652.         }
  653.         set compareResult [string compare [lindex $list1 0] [lindex $list2 0]]
  654.  
  655.         if {$compareResult < 0} {
  656.             lappend list1Result [lvarpop list1]
  657.             continue
  658.         }
  659.         if {$compareResult > 0} {
  660.             lappend list2Result [lvarpop list2]
  661.             continue
  662.         }
  663.         lappend intersectList [lvarpop list1]
  664.         lvarpop list2
  665.     }
  666.     return [list $list1Result $intersectList $list2Result]
  667. }
  668.  
  669. proc intersect {list1 list2} {
  670.     set intersectList ""
  671.  
  672.     set list1 [lsort $list1]
  673.     set list2 [lsort $list2]
  674.  
  675.     while {1} {
  676.         if {[lempty $list1] || [lempty $list2]} break
  677.  
  678.         set compareResult [string compare [lindex $list1 0] [lindex $list2 0]]
  679.  
  680.         if {$compareResult < 0} {
  681.             lvarpop list1
  682.             continue
  683.         }
  684.  
  685.         if {$compareResult > 0} {
  686.             lvarpop list2
  687.             continue
  688.         }
  689.  
  690.         lappend intersectList [lvarpop list1]
  691.         lvarpop list2
  692.     }
  693.     return $intersectList
  694. }
  695.  
  696.  
  697.  
  698. #@package: TclX-showproc showproc
  699.  
  700. proc showproc args {
  701.     if [lempty $args] {
  702.         set args [info procs]
  703.     }
  704.     set out {}
  705.  
  706.     foreach procname $args {
  707.         if [lempty [info procs $procname]] {
  708.             auto_load $procname
  709.         }
  710.         set arglist [info args $procname]
  711.         set nargs {}
  712.         while {[llength $arglist] > 0} {
  713.             set varg [lvarpop arglist 0]
  714.             if [info default $procname $varg defarg] {
  715.                 lappend nargs [list $varg $defarg]
  716.             } else {
  717.                 lappend nargs $varg
  718.             }
  719.         }
  720.         append out "proc $procname [list $nargs] \{[info body $procname]\}\n"
  721.     }
  722.     return $out
  723. }
  724.  
  725. #@package: TclX-stringfile_functions read_file write_file
  726.  
  727. proc read_file {fileName args} {
  728.     if {$fileName == "-nonewline"} {
  729.         set flag $fileName
  730.         set fileName [lvarpop args]
  731.     } else {
  732.         set flag {}
  733.     }
  734.     set fp [open $fileName]
  735.     set stat [catch {
  736.         eval read $flag $fp $args
  737.     } result]
  738.     close $fp
  739.     if {$stat != 0} {
  740.         global errorInfo errorCode
  741.         error $result $errorInfo $errorCode
  742.     }
  743.     return $result
  744.  
  745. proc write_file {fileName args} {
  746.     set fp [open $fileName w]
  747.     
  748.     set stat [catch {
  749.         foreach string $args {
  750.             puts $fp $string
  751.         }
  752.     } result]
  753.     close $fp
  754.     if {$stat != 0} {
  755.         global errorInfo errorCode
  756.         error $result $errorInfo $errorCode
  757.     }
  758. }
  759.  
  760.  
  761. #@package: TclX-libraries searchpath auto_load_file
  762.  
  763. proc searchpath {pathlist file} {
  764.     foreach dir $pathlist {
  765.         if {$dir == ""} {set dir .}
  766.         if {[catch {file exists $dir/$file} result] == 0 && $result}  {
  767.             return $dir/$file
  768.         }
  769.     }
  770.     return {}
  771. }
  772.  
  773. proc auto_load_file {name} {
  774.     global auto_path errorCode
  775.     if {[string first / $name] >= 0} {
  776.         return  [uplevel 1 source $name]
  777.     }
  778.     set where [searchpath $auto_path $name]
  779.     if [lempty $where] {
  780.         error "couldn't find $name in any directory in auto_path"
  781.     }
  782.     uplevel 1 source $where
  783. }
  784.  
  785. #@package: TclX-lib-list auto_packages auto_commands
  786.  
  787.  
  788. proc auto_packages {{option {}}} {
  789.     global auto_pkg_index
  790.  
  791.     auto_load  ;# Make sure all indexes are loaded.
  792.     if ![info exists auto_pkg_index] {
  793.         return {}
  794.     }
  795.     
  796.     set packList [array names auto_pkg_index] 
  797.     if [lempty $option] {
  798.         return $packList
  799.     }
  800.  
  801.     if {$option != "-files"} {
  802.         error "Unknow option \"$option\", expected \"-files\""
  803.     }
  804.     set locList {}
  805.     foreach pack $packList {
  806.         lappend locList [list $pack [lindex $auto_pkg_index($pack) 0]]
  807.     }
  808.     return $locList
  809. }
  810.  
  811.  
  812. proc auto_commands {{option {}}} {
  813.     global auto_index
  814.  
  815.     auto_load  ;# Make sure all indexes are loaded.
  816.     if ![info exists auto_index] {
  817.         return {}
  818.     }
  819.     
  820.     set cmdList [array names auto_index] 
  821.     if [lempty $option] {
  822.         return $cmdList
  823.     }
  824.  
  825.     if {$option != "-loaders"} {
  826.         error "Unknow option \"$option\", expected \"-loaders\""
  827.     }
  828.     set loadList {}
  829.     foreach cmd $cmdList {
  830.         lappend loadList [list $cmd $auto_index($cmd)]
  831.     }
  832.     return $loadList
  833. }
  834.  
  835. #@package: TclX-ucblib auto_reset auto_mkindex
  836.  
  837.  
  838. proc auto_reset {} {
  839.     global auto_execs auto_index auto_oldpath
  840.     foreach p [info procs] {
  841.     if {[info exists auto_index($p)] && ($p != "unknown")
  842.         && ![string match auto_* $p]} {
  843.         rename $p {}
  844.     }
  845.     }
  846.     catch {unset auto_execs}
  847.     catch {unset auto_index}
  848.     catch {unset auto_oldpath}
  849.     catch {unset auto_pkg_index}
  850.     set auto_index(buildpackageindex) {source [info library]/buildidx.tcl}
  851.     return
  852. }
  853.  
  854.  
  855. proc auto_mkindex {dir files} {
  856.     global errorCode errorInfo
  857.     set oldDir [pwd]
  858.     cd $dir
  859.     set dir [pwd]
  860.     append index "# Tcl autoload index file, version 2.0\n"
  861.     append index "# This file is generated by the \"auto_mkindex\" command\n"
  862.     append index "# and sourced to set up indexing information for one or\n"
  863.     append index "# more commands.  Typically each line is a command that\n"
  864.     append index "# sets an element in the auto_index array, where the\n"
  865.     append index "# element name is the name of a command and the value is\n"
  866.     append index "# a script that loads the command.\n\n"
  867.     foreach file [glob $files] {
  868.     set f ""
  869.     set error [catch {
  870.         set f [open $file]
  871.         while {[gets $f line] >= 0} {
  872.         if [regexp {^proc[     ]+([^     ]*)} $line match procName] {
  873.             append index "set [list auto_index($procName)]"
  874.             append index " \"source \$dir/$file\"\n"
  875.         }
  876.         }
  877.         close $f
  878.     } msg]
  879.     if $error {
  880.         set code $errorCode
  881.         set info $errorInfo
  882.         catch [close $f]
  883.         cd $oldDir
  884.         error $msg $info $code
  885.     }
  886.     }
  887.     set f [open tclIndex w]
  888.     puts $f $index nonewline
  889.     close $f
  890.     cd $oldDir
  891. }
  892.  
  893.  
  894. #@package: TclX-fmath acos asin atan ceil cos cosh exp fabs floor log log10 \
  895.            sin sinh sqrt tan tanh fmod pow atan2 abs double int round
  896.  
  897. proc acos  x {uplevel [list expr acos($x)]}
  898. proc asin  x {uplevel [list expr asin($x)]}
  899. proc atan  x {uplevel [list expr atan($x)]}
  900. proc ceil  x {uplevel [list expr ceil($x)]}
  901. proc cos   x {uplevel [list expr cos($x)]}
  902. proc cosh  x {uplevel [list expr cosh($x)]}
  903. proc exp   x {uplevel [list expr exp($x)]}
  904. proc fabs  x {uplevel [list expr abs($x)]}
  905. proc floor x {uplevel [list expr floor($x)]}
  906. proc log   x {uplevel [list expr log($x)]}
  907. proc log10 x {uplevel [list expr log10($x)]}
  908. proc sin   x {uplevel [list expr sin($x)]}
  909. proc sinh  x {uplevel [list expr sinh($x)]}
  910. proc sqrt  x {uplevel [list expr sqrt($x)]}
  911. proc tan   x {uplevel [list expr tan($x)]}
  912. proc tanh  x {uplevel [list expr tanh($x)]}
  913.  
  914. proc fmod {x n} {uplevel [list expr fmod($x,$n)]}
  915. proc pow {x n} {uplevel [list expr pow($x,$n)]}
  916.  
  917.  
  918. proc atan2  x {uplevel [list expr atan2($x)]}
  919. proc abs    x {uplevel [list expr abs($x)]}
  920. proc double x {uplevel [list expr double($x)]}
  921. proc int    x {uplevel [list expr int($x)]}
  922. proc round  x {uplevel [list expr round($x)]}
  923.  
  924.  
  925. #@package: TclX-shell tclx_unknown2 auto_execok
  926.  
  927.  
  928. proc tclx_unknown2 cmd {
  929.     global tcl_interactive auto_noexec
  930.  
  931.     set name [lindex $cmd 0]
  932.  
  933.     if ![info exists auto_noexec] {
  934.         if [auto_execok $name] {
  935.             if {!$tcl_interactive || ([info level] > 2) ||
  936.                 [info script] != ""} {
  937.                 error "Auto execution of Unix commands only supported as interactive commands.\nUse \"exec\" to execute \"$name\""
  938.             }
  939.             uplevel 2 system [list $cmd]
  940.             return
  941.         }
  942.     }
  943.  
  944.     if {!$tcl_interactive || ([info level] > 2) || [info script] != ""} {
  945.         error "invalid command name \"$name\""
  946.     }
  947.  
  948.  
  949.     if {([info level] == 2) && ([info script] == "")} {
  950.         if {$name == "!!"} {
  951.             return [uplevel 2 {history redo}]
  952.         }
  953.         if [regexp {^!(.+)$} $name dummy event] {
  954.             return [uplevel 2 [list history redo $event]]
  955.         }
  956.         if [regexp {^\^([^^]*)\^([^^]*)\^?$} $name dummy old new] {
  957.             return [uplevel 2 [list history substitute $old $new]]
  958.         }
  959.         set cmds [info commands $name*]
  960.         if {[llength $cmds] == 1} {
  961.             return [uplevel 2 [lreplace $cmd 0 0 $cmds]]
  962.         }
  963.         if {[llength $cmds] != 0} {
  964.             if {$name == ""} {
  965.                 return -code error "empty command name \"\""
  966.             } else {
  967.                 return -code error \
  968.                         "ambiguous command name \"$name\": [lsort $cmds]"
  969.             }
  970.         }
  971.     }
  972.     error "invalid command name \"$name\""
  973. }
  974.  
  975.  
  976.  
  977. proc auto_execok name {
  978.     global auto_execs env
  979.  
  980.     if [info exists auto_execs($name)] {
  981.         return $auto_execs($name)
  982.     }
  983.     set auto_execs($name) 0
  984.     if {[string first / $name] >= 0} {
  985.     if {[file executable $name] && ![file isdirectory $name]} {
  986.         puts "special, ok!"
  987.         set auto_execs($name) 1
  988.     }
  989.     return $auto_execs($name)
  990.     }
  991.     foreach dir [split $env(PATH) :] {
  992.         if {[file executable $dir/$name] && ![file isdirectory $dir/$name]} {
  993.             set auto_execs($name) 1
  994.             return 1
  995.         }
  996.     }
  997.     return 0
  998. }
  999.  
  1000. #@package: TclX-buildhelp buildhelp
  1001.  
  1002. proc TruncFileName {pathName} {
  1003.     global truncFileNames
  1004.  
  1005.     if {!$truncFileNames} {
  1006.         return $pathName}
  1007.     set fileName [file tail $pathName]
  1008.     if {"[crange $fileName 0 3]" == "Tcl_"} {
  1009.         set fileName [crange $fileName 4 end]}
  1010.     set fileName [crange $fileName 0 13]
  1011.     return "[file dirname $pathName]/$fileName"
  1012. }
  1013.  
  1014.  
  1015. proc EnsureDirs {filePath} {
  1016.     set dirPath [file dirname $filePath]
  1017.     if [file exists $dirPath] return
  1018.     foreach dir [split $dirPath /] {
  1019.         lappend dirList $dir
  1020.         set partPath [join $dirList /]
  1021.         if [file exists $partPath] continue
  1022.  
  1023.         mkdir $partPath
  1024.         chmod u=rwx,go=rx $partPath
  1025.     }
  1026. }
  1027.  
  1028.  
  1029. proc CreateFilterNroffManPageContext {} {
  1030.     global filterNroffManPageContext
  1031.  
  1032.     set filterNroffManPageContext [scancontext create]
  1033.  
  1034.  
  1035.     scanmatch $filterNroffManPageContext {@@@BUILDHELP@@@} {
  1036.         catch {unset prev2Blanks}
  1037.         catch {unset prev1Line}
  1038.         catch {unset prev1Blanks}
  1039.         set nukeBlanks {}
  1040.     }
  1041.  
  1042.  
  1043.     scanmatch $filterNroffManPageContext {$^} {
  1044.         if ![info exists nukeBlanks] {
  1045.             append prev1Blanks \n
  1046.         }
  1047.     }
  1048.  
  1049.  
  1050.     scanmatch $filterNroffManPageContext {
  1051.         catch {unset nukeBlanks}
  1052.         if [info exists prev2Line] {
  1053.             puts $outFH $prev2Line
  1054.             unset prev2Line
  1055.         }
  1056.         if [info exists prev2Blanks] {
  1057.             puts $outFH $prev2Blanks nonewline
  1058.             unset prev2Blanks
  1059.         }
  1060.         if [info exists prev1Line] {
  1061.             set prev2Line $prev1Line
  1062.         }
  1063.         set prev1Line $matchInfo(line)
  1064.         if [info exists prev1Blanks] {
  1065.             set prev2Blanks $prev1Blanks
  1066.             unset prev1Blanks
  1067.         }
  1068.     }
  1069. }
  1070.  
  1071.  
  1072. proc FilterNroffManPage {inFH outFH} {
  1073.     global filterNroffManPageContext
  1074.  
  1075.     if ![info exists filterNroffManPageContext] {
  1076.         CreateFilterNroffManPageContext
  1077.     }
  1078.  
  1079.     scanfile $filterNroffManPageContext $inFH
  1080.  
  1081.     if [info exists prev2Line] {
  1082.         puts $outFH $prev2Line
  1083.     }
  1084. }
  1085.  
  1086.  
  1087. proc CreateExtractNroffHeaderContext {} {
  1088.     global extractNroffHeaderContext
  1089.  
  1090.     set extractNroffHeaderContext [scancontext create]
  1091.  
  1092.     scanmatch $extractNroffHeaderContext {'\\"@endheader[     ]*$} {
  1093.         break
  1094.     }
  1095.     scanmatch $extractNroffHeaderContext {'\\"@:} {
  1096.         append nroffHeader "[crange $matchInfo(line) 5 end]\n"
  1097.     }
  1098.     scanmatch $extractNroffHeaderContext {
  1099.         append nroffHeader "$matchInfo(line)\n"
  1100.     }
  1101. }
  1102.  
  1103.  
  1104. proc ExtractNroffHeader {manPageFH} {
  1105.     global extractNroffHeaderContext nroffHeader
  1106.  
  1107.     if ![info exists extractNroffHeaderContext] {
  1108.         CreateExtractNroffHeaderContext
  1109.     }
  1110.     scanfile $extractNroffHeaderContext $manPageFH
  1111. }
  1112.  
  1113.  
  1114.  
  1115. proc CreateExtractNroffHelpContext {} {
  1116.     global extractNroffHelpContext
  1117.  
  1118.     set extractNroffHelpContext [scancontext create]
  1119.  
  1120.     scanmatch $extractNroffHelpContext {^'\\"@endhelp[     ]*$} {
  1121.         break
  1122.     }
  1123.  
  1124.     scanmatch $extractNroffHelpContext {^'\\"@brief:} {
  1125.         if $foundBrief {
  1126.             error {Duplicate "@brief:" entry}
  1127.         }
  1128.         set foundBrief 1
  1129.         puts $briefHelpFH "$helpName\t[csubstr $matchInfo(line) 11 end]"
  1130.         continue
  1131.     }
  1132.  
  1133.     scanmatch $extractNroffHelpContext {^'\\"@:} {
  1134.         puts $nroffFH  [csubstr $matchInfo(line) 5 end]
  1135.         continue
  1136.     }
  1137.     scanmatch $extractNroffHelpContext {^'\\"@help:} {
  1138.         error {"@help" found within another help section"}
  1139.     }
  1140.     scanmatch $extractNroffHelpContext {
  1141.         puts $nroffFH $matchInfo(line)
  1142.     }
  1143. }
  1144.  
  1145.  
  1146. proc ExtractNroffHelp {manPageFH manLine} {
  1147.     global helpDir nroffHeader briefHelpFH colArgs
  1148.     global extractNroffHelpContext
  1149.  
  1150.     if ![info exists extractNroffHelpContext] {
  1151.         CreateExtractNroffHelpContext
  1152.     }
  1153.  
  1154.     set helpName [string trim [csubstr $manLine 9 end]]
  1155.     set helpFile [TruncFileName "$helpDir/$helpName"]
  1156.     if [file exists $helpFile] {
  1157.         error "Help file already exists: $helpFile"
  1158.     }
  1159.     EnsureDirs $helpFile
  1160.  
  1161.     set tmpFile "[file dirname $helpFile]/tmp.[id process]"
  1162.  
  1163.     echo "    creating help file $helpName"
  1164.  
  1165.     set nroffFH [open "| nroff -man | col $colArgs > $tmpFile" w]
  1166.  
  1167.     puts $nroffFH {.TH @@@BUILDHELP@@@ 1}
  1168.  
  1169.     set foundBrief 0
  1170.     scanfile $extractNroffHelpContext $manPageFH
  1171.  
  1172.  
  1173.     set stat [catch {
  1174.         close $nroffFH
  1175.     } msg]
  1176.     if $stat {
  1177.         puts stderr "nroff: $msg"
  1178.     }
  1179.  
  1180.     set tmpFH [open $tmpFile r]
  1181.     set helpFH [open $helpFile w]
  1182.  
  1183.     FilterNroffManPage $tmpFH $helpFH
  1184.  
  1185.     close $tmpFH
  1186.     close $helpFH
  1187.  
  1188.     unlink $tmpFile
  1189.     chmod a-w,a+r $helpFile
  1190. }
  1191.  
  1192.  
  1193. proc CreateExtractScriptHelpContext {} {
  1194.     global extractScriptHelpContext
  1195.  
  1196.     set extractScriptHelpContext [scancontext create]
  1197.  
  1198.     scanmatch $extractScriptHelpContext {^#@endhelp[     ]*$} {
  1199.         break
  1200.     }
  1201.  
  1202.     scanmatch $extractScriptHelpContext {^#@brief:} {
  1203.         if $foundBrief {
  1204.             error {Duplicate "@brief" entry}
  1205.         }
  1206.         set foundBrief 1
  1207.         puts $briefHelpFH "$helpName\t[csubstr $matchInfo(line) 9 end]"
  1208.         continue
  1209.     }
  1210.  
  1211.     scanmatch $extractScriptHelpContext {^#@help:} {
  1212.         error {"@help" found within another help section"}
  1213.     }
  1214.     scanmatch $extractScriptHelpContext {
  1215.         if {[clength $matchInfo(line)] > 1} {
  1216.             puts $helpFH " [csubstr $matchInfo(line) 1 end]"
  1217.         } else {
  1218.             puts $helpFH $matchInfo(line)
  1219.         }
  1220.     }
  1221. }
  1222.  
  1223.  
  1224. proc ExtractScriptHelp {ScriptPageFH ScriptLine} {
  1225.     global helpDir briefHelpFH
  1226.     global extractScriptHelpContext
  1227.  
  1228.     if ![info exists extractScriptHelpContext] {
  1229.         CreateExtractScriptHelpContext
  1230.     }
  1231.  
  1232.     set helpName [string trim [csubstr $ScriptLine 7 end]]
  1233.     set helpFile "$helpDir/$helpName"
  1234.     if {[file exists $helpFile]} {
  1235.         error "Help file already exists: $helpFile"
  1236.     }
  1237.     EnsureDirs $helpFile
  1238.  
  1239.     echo "    creating help file $helpName"
  1240.  
  1241.     set helpFH [open $helpFile w]
  1242.  
  1243.     set foundBrief 0
  1244.     scanfile $extractScriptHelpContext $manPageFH
  1245.  
  1246.     close $helpFH
  1247.     chmod a-w,a+r $helpFile
  1248. }
  1249.  
  1250.  
  1251. proc ProcessNroffFile {pathName} {
  1252.    global nroffScanCT scriptScanCT nroffHeader
  1253.  
  1254.    set fileName [file tail $pathName]
  1255.  
  1256.    set nroffHeader {}
  1257.    set manPageFH [open $pathName r]
  1258.    set matchInfo(fileName) [file tail $pathName]
  1259.  
  1260.    echo "    scanning $pathName"
  1261.  
  1262.    scanfile $nroffScanCT $manPageFH
  1263.  
  1264.    close $manPageFH
  1265. }
  1266.  
  1267.  
  1268. proc ProcessTclScript {pathName} {
  1269.    global scriptScanCT nroffHeader
  1270.  
  1271.    set scriptFH [open "$pathName" r]
  1272.    set matchInfo(fileName) [file tail $pathName]
  1273.  
  1274.    echo "    scanning $pathName"
  1275.    scanfile $scriptScanCT $scriptFH
  1276.  
  1277.    close $scriptFH
  1278. }
  1279.  
  1280.  
  1281. proc buildhelp {helpDirPath briefFile sourceFiles} {
  1282.     global helpDir truncFileNames nroffScanCT
  1283.     global scriptScanCT briefHelpFH colArgs
  1284.  
  1285.     echo ""
  1286.     echo "Begin building help tree"
  1287.  
  1288.     if {[system {col -bx </dev/null >/dev/null 2>&1}] != 0} {
  1289.         set colArgs {-b}
  1290.     } else {
  1291.         set colArgs {-bx}
  1292.     }
  1293.     set helpDir $helpDirPath
  1294.     if {![file exists $helpDir]} {
  1295.         mkdir $helpDir
  1296.     }
  1297.  
  1298.     if {![file isdirectory $helpDir]} {
  1299.         error [concat "$helpDir is not a directory or does not exist. "  
  1300.                       "This should be the help root directory"]
  1301.     }
  1302.         
  1303.     set status [catch {set tmpFH [open $helpDir/AVeryVeryBigFileName w]}]
  1304.     if {$status != 0} {
  1305.         set truncFileNames 1
  1306.     } else {
  1307.         close $tmpFH
  1308.         unlink $helpDir/AVeryVeryBigFileName
  1309.         set truncFileNames 0
  1310.     }
  1311.  
  1312.     set nroffScanCT [scancontext create]
  1313.  
  1314.     scanmatch $nroffScanCT {'\\"@help:} {
  1315.         ExtractNroffHelp $matchInfo(handle) $matchInfo(line)
  1316.         continue
  1317.     }
  1318.  
  1319.     scanmatch $nroffScanCT {^'\\"@header} {
  1320.         ExtractNroffHeader $matchInfo(handle)
  1321.         continue
  1322.     }
  1323.     scanmatch $nroffScanCT {^'\\"@endhelp} {
  1324.         error [concat {@endhelp" without corresponding "@help:"} \
  1325.                  ", offset = $matchInfo(offset)"]
  1326.     }
  1327.     scanmatch $nroffScanCT {^'\\"@brief} {
  1328.         error [concat {"@brief" without corresponding "@help:"} \
  1329.                  ", offset = $matchInfo(offset)"]
  1330.     }
  1331.  
  1332.     set scriptScanCT [scancontext create]
  1333.     scanmatch $scriptScanCT {^#@help:} {
  1334.         ExtractScriptHelp $matchInfo(handle) $matchInfo(line)
  1335.     }
  1336.  
  1337.     if {[file extension $briefFile] != ".brf"} {
  1338.         error "Brief file \"$briefFile\" must have an extension \".brf\""
  1339.     }
  1340.     if [file exists $helpDir/$briefFile] {
  1341.         error "Brief file \"$helpDir/$briefFile\" already exists"
  1342.     }
  1343.     set briefHelpFH [open "|sort > $helpDir/$briefFile" w]
  1344.  
  1345.     foreach manFile [glob $sourceFiles] {
  1346.         set ext [file extension $manFile]
  1347.         if {$ext == ".tcl" || $ext == ".tlib"} {
  1348.             set status [catch {ProcessTclScript $manFile} msg]
  1349.         } else {
  1350.             set status [catch {ProcessNroffFile $manFile} msg]
  1351.         }
  1352.         if {$status != 0} {
  1353.             global errorInfo errorCode
  1354.             error "Error extracting help from: $manFile" $errorInfo $errorCode
  1355.         }
  1356.     }
  1357.  
  1358.     close $briefHelpFH
  1359.     chmod a-w,a+r $helpDir/$briefFile
  1360.     echo "Completed extraction of help files"
  1361. }
  1362.  
  1363.  
  1364. #@package: Tcl-parray parray
  1365.  
  1366.  
  1367. proc parray a {
  1368.     upvar 1 $a array
  1369.     if [catch {array size array}] {
  1370.     error "\"$a\" isn't an array"
  1371.     }
  1372.     set maxl 0
  1373.     foreach name [lsort [array names array]] {
  1374.     if {[string length $name] > $maxl} {
  1375.         set maxl [string length $name]
  1376.     }
  1377.     }
  1378.     set maxl [expr {$maxl + [string length $a] + 2}]
  1379.     foreach name [lsort [array names array]] {
  1380.     set nameString [format %s(%s) $a $name]
  1381.     puts stdout [format "%-*s = %s" $maxl $nameString $array($name)]
  1382.     }
  1383. }
  1384.